package xiongwei.jiang.service.impl;

import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import xiongwei.jiang.constant.WXWorkApiConstants;
import xiongwei.jiang.model.dto.*;
import xiongwei.jiang.properties.WXWorkProperties;
import xiongwei.jiang.service.EhcacheService;
import xiongwei.jiang.service.WXWorkService;
import xiongwei.jiang.util.MathUtils;
import xiongwei.jiang.util.StringUtils;
import xiongwei.jiang.wxwork.AesException;
import xiongwei.jiang.wxwork.WXBizMsgCrypt;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * 企业微信服务接口实现类
 *
 * @author 天之蓝
 */
public class WXWorkServiceImpl implements WXWorkService {
    private static final Logger LOGGER = LoggerFactory.getLogger(WXWorkServiceImpl.class);

    private final EhcacheService ehcacheService;

    private final RestTemplate restTemplate;

    private final WXWorkProperties wxWorkProperties;

    public static final Gson GSON = new Gson();

    public static final String WXWORK_ALIAS = "cache:wxwork:7200s";

    public WXWorkServiceImpl(EhcacheService ehcacheService, RestTemplate restTemplate, WXWorkProperties wxWorkProperties) {
        this.ehcacheService = ehcacheService;
        this.restTemplate = restTemplate;
        this.wxWorkProperties = wxWorkProperties;
        this.init();
    }

    private void init() {
        if (StringUtils.isBlank(wxWorkProperties.getCorpId())) {
            throw new RuntimeException("wxwork 初始化异常：缺少重要参数（参数配置请参考 README.md，项目地址：https://gitee.com/jiangxiongwei/wxwork-spring-boot-starter）");
        }
    }

    @Override
    public UserIdResponseDTO getUserId(Integer appIndex, String code) {
        String url = String.format(WXWorkApiConstants.GET_USER_ID_URL, getTokenCache(appIndex), code);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] userId 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UserIdResponseDTO.class);
    }

    @Override
    public UserInfoResponseDTO getUserInfo(Integer appIndex, String userId) {
        String url = String.format(WXWorkApiConstants.GET_USER_INFO_URL, getTokenCache(appIndex), userId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] userInfo 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UserInfoResponseDTO.class);
    }

    @Override
    public DepartmentDataResponseDTO getDepartmentData(Integer appIndex, Integer departmentId) {
        String url = String.format(WXWorkApiConstants.GET_DEPARTMENT_DATA_URL, getTokenCache(appIndex), departmentId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] departmentData 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), DepartmentDataResponseDTO.class);
    }

    @Override
    public DepartmentUserResponseDTO getDepartmentUser(Integer appIndex, Integer departmentId, Integer fetchChild) {
        String url = String.format(WXWorkApiConstants.GET_DEPARTMENT_USER_URL, getTokenCache(appIndex), departmentId, fetchChild);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] departmentUser 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), DepartmentUserResponseDTO.class);
    }

    @Override
    public TagDataResponseDTO getTagData() {
        String url = String.format(WXWorkApiConstants.GET_TAG_DATA_URL, getTokenCache(-1));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [-1] tagData 返回的信息 = {}", responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), TagDataResponseDTO.class);
    }

    @Override
    public TagUserResponseDTO getTagUser(Integer tagId) {
        String url = String.format(WXWorkApiConstants.GET_TAG_USER_URL, getTokenCache(-1), tagId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [-1] tagUser 返回的信息 = " + responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), TagUserResponseDTO.class);
    }

    @Override
    public AppMsgResponseDTO sendTextMsg(Integer appIndex, String userId, String content) {
        TextMsgRequestDTO requestDTO = new TextMsgRequestDTO();
        requestDTO.setTouser(userId);
        requestDTO.setAgentid(wxWorkProperties.getApp().getAgentId().get(appIndex));
        TextMsgRequestDTO.Text text = new TextMsgRequestDTO.Text();
        text.setContent(content);
        requestDTO.setText(text);
        String json = GSON.toJson(requestDTO);
        String url = String.format(WXWorkApiConstants.POST_SEND_MSG_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.trace("发送 [{}] 消息通知 {} 返回的信息 = {}", appIndex, userId, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), AppMsgResponseDTO.class);
    }

    @Override
    public AppMsgResponseDTO sendTextMsg(Integer appIndex, List<String> userIdList, String content) {
        int count = userIdList.size();
        StringBuffer stringBuffer = new StringBuffer();
        for (int i = 0; i < count - 1; i++) {
            stringBuffer.append(userIdList.get(i)).append("|");
        }
        stringBuffer.append(userIdList.get(count - 1));
        return sendTextMsg(appIndex, stringBuffer.toString(), content);
    }

    @Override
    public UploadTempResponseDTO uploadTemp(Integer appIndex, UploadTypeEnum typeEnum, String filePath) throws IOException {
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        FileSystemResource fileSystemResource = new FileSystemResource(filePath);
        form.add("file", fileSystemResource);
        String url = String.format(WXWorkApiConstants.POST_UPLOAD_TEMP_URL, getTokenCache(appIndex), typeEnum.code);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        requestHeaders.setContentLength(fileSystemResource.contentLength());
        requestHeaders.setContentDispositionFormData("media", fileSystemResource.getFilename());
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.trace("上传 [{}] uploadTemp 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UploadTempResponseDTO.class);
    }

    @Getter
    @AllArgsConstructor
    public enum UploadTypeEnum {
        MEDIA_IMAGE("image", "图片"),
        MEDIA_VOICE("voice", "语音"),
        MEDIA_VIDEO("video", "视频"),
        FILE("file", "普通文件");

        private final String code;

        private final String explain;
    }

    @Override
    public UploadImageResponseDTO uploadImage(Integer appIndex, String filePath) throws IOException {
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();
        FileSystemResource fileSystemResource = new FileSystemResource(filePath);
        form.add("file", fileSystemResource);
        String url = String.format(WXWorkApiConstants.POST_UPLOAD_IMAGE_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
        requestHeaders.setContentLength(fileSystemResource.contentLength());
        requestHeaders.setContentDispositionFormData("image", fileSystemResource.getFilename());
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.trace("上传 [{}] uploadImage 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), UploadImageResponseDTO.class);
    }

    @Override
    public byte[] downloadTemp(Integer appIndex, String mediaId) {
        String url = String.format(WXWorkApiConstants.GET_DOWNLOAD_TEMP_URL, getTokenCache(appIndex), mediaId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class);
        if (Objects.requireNonNull(responseEntity.getHeaders().getContentType()).getSubtype().equals("json")) {
            LOGGER.error("下载 [{}] downloadTemp 返回的信息 = {}", appIndex, new String(Objects.requireNonNull(responseEntity.getBody()), StandardCharsets.UTF_8));
            return null;
        }
        return responseEntity.getBody();
    }

    @Override
    public byte[] downloadVoice(Integer appIndex, String mediaId) {
        String url = String.format(WXWorkApiConstants.GET_DOWNLOAD_VOICE_URL, getTokenCache(appIndex), mediaId);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class);
        if (Objects.requireNonNull(responseEntity.getHeaders().getContentType()).getSubtype().equals("json")) {
            LOGGER.error("下载 [{}] downloadVoice 返回的信息 = {}", appIndex, new String(Objects.requireNonNull(responseEntity.getBody()), StandardCharsets.UTF_8));
            return null;
        }
        return responseEntity.getBody();
    }

    @Override
    public CheckinDataResponseDTO getCheckinData(Integer openCheckinDataType, Long startTime, Long endTime, List<String> userIdList) {
        CheckinDataRequestDTO requestDTO = new CheckinDataRequestDTO();
        requestDTO.setOpencheckindatatype(openCheckinDataType);
        requestDTO.setStarttime(startTime);
        requestDTO.setEndtime(endTime);
        requestDTO.setUseridlist(userIdList);
        String json = GSON.toJson(requestDTO);
        String url = String.format(WXWorkApiConstants.POST_CHECKIN_DATA_URL, getTokenCache(-2));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(json, requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        LOGGER.trace("获取 [-2] checkinData 返回的信息 = {}", responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), CheckinDataResponseDTO.class);
    }

    public String getTokenCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_token");
        LOGGER.debug("获取 [{}] appToken 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getToken(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_token", cache, 7200L);
            LOGGER.debug("设置 [{}] appToken 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 token
     *
     * @param appIndex 索引
     * @return token
     * @author 天之蓝
     */
    private String getToken(Integer appIndex) {
        String secret;
        switch (appIndex) {
            case -2:
                // 打卡
                secret = wxWorkProperties.getCheckin().getSecret();
                break;
            case -1:
                // 通讯录
                secret = wxWorkProperties.getAddrBook().getSecret();
                break;
            default:
                // 自建应用
                secret = wxWorkProperties.getApp().getSecret().get(appIndex);
                break;
        }
        String url = String.format(WXWorkApiConstants.GET_TOKEN_URL, wxWorkProperties.getCorpId(), secret);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] appToken 返回的信息 = {}", appIndex, responseEntity.getBody());
        TokenResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TokenResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            LOGGER.error("获取 [{}] appToken 失败", appIndex);
            return null;
        }
        return responseDTO.getAccess_token();
    }

    @Override
    public String getJsapiTicketCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_ticket");
        LOGGER.debug("获取 [{}] jsapiTicket 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getJsapiTicket(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_ticket", cache, 7200L);
            LOGGER.debug("设置 [{}] jsapiTicket 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 jsapi_ticket
     *
     * @param appIndex 索引
     * @return jsapiTicket
     * @author 天之蓝
     */
    private String getJsapiTicket(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_JSAPI_TICKET_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] jsapiTicket 返回的信息 = {}", appIndex, responseEntity.getBody());
        TicketResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TicketResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            LOGGER.error("获取 [{}] jsapiTicket 失败", appIndex);
            return null;
        }
        return responseDTO.getTicket();
    }

    @Override
    public String getJsapiTicketAgentCache(Integer appIndex) {
        String cache = ehcacheService.getStringCache(WXWORK_ALIAS, appIndex + "_ticket_agent");
        LOGGER.debug("获取 [{}] jsapiTicketAgent 缓存 = {}", appIndex, cache);
        if (cache == null) {
            cache = getJsapiTicketAgent(appIndex);
            ehcacheService.setStringCache(WXWORK_ALIAS, appIndex + "_ticket_agent", cache, 7200L);
            LOGGER.debug("设置 [{}] jsapiTicketAgent 缓存 = {}", appIndex, cache);
        }
        return cache;
    }

    /**
     * 获取 jsapi_ticket
     *
     * @param appIndex 索引
     * @return jsapiTicketAgent
     * @author 天之蓝
     */
    private String getJsapiTicketAgent(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_JSAPI_TICKET_AGENT_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.debug("获取 [{}] jsapiTicketAgent 返回的信息 = {}", appIndex, responseEntity.getBody());
        TicketResponseDTO responseDTO = GSON.fromJson(responseEntity.getBody(), TicketResponseDTO.class);
        if (responseDTO.getErrcode() != 0) {
            LOGGER.error("获取 [{}] jsapiTicketAgent 失败", appIndex);
            return null;
        }
        return responseDTO.getTicket();
    }

    @Override
    public boolean verifyUrl(Integer appIndex, String msg_signature, String timestamp, String nonce, String echostr, HttpServletResponse response) {
        try {
            String data = new WXBizMsgCrypt(
                    wxWorkProperties.getCallback().getToken().get(appIndex),
                    wxWorkProperties.getCallback().getEncodingAesKey().get(appIndex),
                    wxWorkProperties.getCorpId()
            ).VerifyURL(msg_signature, timestamp, nonce, echostr);
            LOGGER.info("验证 [{}] url 有效性正常", appIndex);
            response.getWriter().print(data);
            LOGGER.info("响应 [{}] 企业微信成功", appIndex);
            return true;
        } catch (AesException | IOException e) {
            LOGGER.error("验证 [{}] url 有效性异常", appIndex, e);
            return false;
        }
    }

    @Override
    public Multimap<String, String> callback(Integer appIndex, String msg_signature, String timestamp, String nonce, String xml, HttpServletResponse response) {
        String data;
        try {
            data = new WXBizMsgCrypt(
                    wxWorkProperties.getCallback().getToken().get(appIndex),
                    wxWorkProperties.getCallback().getEncodingAesKey().get(appIndex),
                    wxWorkProperties.getCorpId()
            ).DecryptMsg(msg_signature, timestamp, nonce, xml);
            LOGGER.info("解析 [{}] xml 明文 = {}", appIndex, data);
            response.getWriter().print("success");
            LOGGER.info("响应 [{}] callback 成功", appIndex);
        } catch (AesException | IOException e) {
            LOGGER.error("处理 [{}] callback 异常", appIndex, e);
            return null;
        }
        return xmlToMultimap(data);
    }

    /**
     * xml 转 map
     *
     * @param xmlStr xml 字符串
     * @return k-v（LinkedHashMultimap，k 为完全限定节点名称，例如：grandparentNodeName.parentNodeName.nodeName）
     * @author 天之蓝
     */
    public static Multimap<String, String> xmlToMultimap(String xmlStr) {
        Multimap<String, String> map = LinkedHashMultimap.create();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlStr.getBytes(StandardCharsets.UTF_8))) {
            SAXReader saxReader = new SAXReader();
            saxReader.setEncoding(StandardCharsets.UTF_8.name());
            listNodes(saxReader.read(byteArrayInputStream).getRootElement(), map);
        } catch (Exception e) {
            LOGGER.error("xml 转 map 异常", e);
            return null;
        }
        return map;
    }

    /**
     * 递归遍历 xml 节点
     *
     * @param node xml 节点
     * @param map  载体
     * @author 天之蓝
     */
    public static void listNodes(Element node, Multimap<String, String> map) {
        // 获取当前节点的内容
        if (StringUtils.isNotBlank(node.getTextTrim())) {
            NodeName nodeName = new NodeName(node.getName());
            getFullyQualifiedNodeName(node, nodeName);
            LOGGER.trace("{} -> {}", nodeName.getNodeName(), node.getText());
            map.put(nodeName.getNodeName(), node.getText());
        }
        // 迭代该节点的所有子节点
        List<Element> elements = node.elements();
        for (Element element : elements) {
            // 使用递归
            listNodes(element, map);
        }
    }

    /**
     * 递归获取完全限定节点名称，以英文点“.”分隔（例如：grandparentNodeName.parentNodeName.nodeName）
     *
     * @param node     节点
     * @param nodeName 自定义节点名称对象
     * @author 天之蓝
     */
    public static void getFullyQualifiedNodeName(Element node, NodeName nodeName) {
        if (node.getParent() == null) {
            return;
        }
        nodeName.setNodeName(node.getParent().getName() + "." + nodeName.getNodeName());
        getFullyQualifiedNodeName(node.getParent(), nodeName);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class NodeName {
        private String nodeName;
    }

    @Override
    public ConfigSignatureResponseDTO getConfigSignature(Integer appIndex, String currentPageUrl) {
        String ticket = getJsapiTicketCache(appIndex);
        String nonceStr = UUID.randomUUID().toString();
        long timestamp = System.currentTimeMillis() / 1000;
        currentPageUrl = dealCurrentPageUrl(currentPageUrl);
        String str = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + currentPageUrl;
        String signature = MathUtils.sha1Hex(str);
        ConfigSignatureResponseDTO responseDTO = new ConfigSignatureResponseDTO();
        responseDTO.setAppId(wxWorkProperties.getCorpId());
        responseDTO.setTimestamp(String.valueOf(timestamp));
        responseDTO.setNonceStr(nonceStr);
        responseDTO.setSignature(signature);
        responseDTO.setJsApiList(Lists.newArrayList("所有JS接口列表：https://work.weixin.qq.com/api/doc/90000/90136/90507"));
        LOGGER.trace("获取 [{}] getConfigSignature = {}", appIndex, responseDTO);
        return responseDTO;
    }

    @Override
    public AgentConfigSignatureResponseDTO getAgentConfigSignature(Integer appIndex, String currentPageUrl) {
        String ticket = getJsapiTicketAgentCache(appIndex);
        String nonceStr = UUID.randomUUID().toString();
        long timestamp = System.currentTimeMillis() / 1000;
        currentPageUrl = dealCurrentPageUrl(currentPageUrl);
        String str = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + currentPageUrl;
        String signature = MathUtils.sha1Hex(str);
        AgentConfigSignatureResponseDTO responseDTO = new AgentConfigSignatureResponseDTO();
        responseDTO.setCorpid(wxWorkProperties.getCorpId());
        responseDTO.setAgentid(wxWorkProperties.getApp().getAgentId().get(appIndex));
        responseDTO.setTimestamp(String.valueOf(timestamp));
        responseDTO.setNonceStr(nonceStr);
        responseDTO.setSignature(signature);
        responseDTO.setJsApiList(Lists.newArrayList("所有JS接口列表：https://work.weixin.qq.com/api/doc/90000/90136/90507"));
        LOGGER.trace("获取 [{}] getAgentConfigSignature = {}", appIndex, responseDTO);
        return responseDTO;
    }

    /**
     * 处理当前页面 url
     *
     * @param currentPageUrl 当前页面 url
     * @return 字符串
     * @author 天之蓝
     */
    private String dealCurrentPageUrl(String currentPageUrl) {
        currentPageUrl = currentPageUrl.split("#")[0];
        int index = currentPageUrl.length() - 1;
        if (currentPageUrl.lastIndexOf("/") == index) {
            currentPageUrl = currentPageUrl.substring(0, index);
        }
        return currentPageUrl;
    }

    @Override
    public CallbackIpResponseDTO getCallbackIp(Integer appIndex) {
        String url = String.format(WXWorkApiConstants.GET_CALLBACK_IP_URL, getTokenCache(appIndex));
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestHeaders);
        HttpEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
        LOGGER.trace("获取 [{}] callbackIp 返回的信息 = {}", appIndex, responseEntity.getBody());
        return GSON.fromJson(responseEntity.getBody(), CallbackIpResponseDTO.class);
    }
}
